﻿using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Configuration.Provider;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web.Configuration;
using System.Web.Hosting;
using System.Web.Security;
using Emerald.Business;
using Emerald.Business.Controllers;

namespace Emerald.WebSite.Extensions.Security
{
    /// <summary>
    /// The Emerald membership provider.
    /// </summary>
    public class EmeraldMembershipProvider : MembershipProvider
    {
        private UserController m_userController;
        private string m_applicationName;
        private bool m_enablePasswordReset;
        private bool m_enablePasswordRetrieval;
        private int m_maxInvalidPasswordAttempts;
        private int m_minRequiredNonAlphanumericCharacters;
        private int m_minRequiredPasswordLength;
        private int m_passwordAttemptWindow;
        private MembershipPasswordFormat m_passwordFormat;
        private string m_passwordStrengthRegularExpression;
        private bool m_requiresQuestionAndAnswer;
        private bool m_requiresUniqueEmail;
        private MachineKeySection machineKey;

		#region Private Properties
		
        private UserController UserController
        {
            get
            {
                if (m_userController == null)
                {
                    m_userController = new UserController();
                }

                return m_userController;
            }
        }
		
		#endregion

		#region Public Methods
		
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="config"></param>
        public override void Initialize(string name, NameValueCollection config)
        {
            #region Validation
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }
            #endregion

            if (name == null || name.Length == 0)
            {
                name = "Emerald";
            }

            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "Emerald membership provider");
            }

            m_applicationName = ProviderHelper.GetConfigValue(config["applicationName"], HostingEnvironment.ApplicationVirtualPath);
            m_enablePasswordReset = Convert.ToBoolean(ProviderHelper.GetConfigValue(config["enablePasswordReset"], "true"));
            m_enablePasswordRetrieval = Convert.ToBoolean(ProviderHelper.GetConfigValue(config["enablePasswordRetrieval"], "false"));
            m_maxInvalidPasswordAttempts = Convert.ToInt32(ProviderHelper.GetConfigValue(config["maxInvalidPasswordAttempts"], "5"));
            m_minRequiredNonAlphanumericCharacters = Convert.ToInt32(ProviderHelper.GetConfigValue(config["minRequiredNonAlphanumericCharacters"], "0"));
            m_minRequiredPasswordLength = Convert.ToInt32(ProviderHelper.GetConfigValue(config["minRequiredPasswordLength"], "6"));
            m_passwordAttemptWindow = Convert.ToInt32(ProviderHelper.GetConfigValue(config["passwordAttemptWindow"], "10"));
            m_passwordFormat = GetPasswordFormat(config["passwordFormat"], "Clear");
            m_passwordStrengthRegularExpression = Convert.ToString(ProviderHelper.GetConfigValue(config["passwordStrengthRegularExpression"], ""));
            m_requiresQuestionAndAnswer = Convert.ToBoolean(ProviderHelper.GetConfigValue(config["requiresQuestionAndAnswer"], "false"));
            m_requiresUniqueEmail = Convert.ToBoolean(ProviderHelper.GetConfigValue(config["requiresUniqueEmail"], "true"));

            Configuration cfg = WebConfigurationManager.OpenWebConfiguration(HostingEnvironment.ApplicationVirtualPath);
            machineKey = (MachineKeySection)cfg.GetSection("system.web/machineKey");

            if (machineKey.ValidationKey.Contains("AutoGenerate"))
            {
                if (PasswordFormat != MembershipPasswordFormat.Clear)
                {
                    throw new ProviderException("Hashed or encrypted passwords are not supported with auto-generated keys.");
                }
            }

            base.Initialize(name, config);
        }

        /// <summary>
        /// 
        /// </summary>
        public override string ApplicationName
        {
            get { return m_applicationName; }
            set { m_applicationName = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="username"></param>
        /// <param name="oldPassword"></param>
        /// <param name="newPassword"></param>
        /// <returns></returns>
        public override bool ChangePassword(string username, string oldPassword, string newPassword)
        {
            #region Validation
            if (String.IsNullOrEmpty(username))
            {
                throw new ArgumentNullException("username");
            }

            if (String.IsNullOrEmpty(oldPassword))
            {
                throw new ArgumentNullException("oldPassword");
            }

            if (String.IsNullOrEmpty(newPassword))
            {
                throw new ArgumentNullException("newPassword");
            }
            #endregion

            if (!ValidateUser(username, oldPassword))
            {
                return false;
            }

            ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, newPassword, true);
            OnValidatingPassword(args);

            if (args.Cancel)
            {
                if (args.FailureInformation != null)
                {
                    throw args.FailureInformation;
                }
                else
                {
                    throw new MembershipPasswordException("Change password canceled due to new password validation failure.");
                }
            }

            User user = UserController.GetUserByUsername(username);

            if (user == null)
            {
                throw new MembershipPasswordException("The supplied user name is not found.");
            }

            user.Password = EncodeString(newPassword);
            UserController.Update(user);

            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="newPasswordQuestion"></param>
        /// <param name="newPasswordAnswer"></param>
        /// <returns></returns>
        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
        {
            #region Validation
            if (String.IsNullOrEmpty(username))
            {
                throw new ArgumentNullException("username");
            }

            if (String.IsNullOrEmpty(password))
            {
                throw new ArgumentNullException("password");
            }

            if (String.IsNullOrEmpty(newPasswordQuestion))
            {
                throw new ArgumentNullException("newPasswordQuestion");
            }

            if (String.IsNullOrEmpty(newPasswordAnswer))
            {
                throw new ArgumentNullException("newPasswordAnswer");
            }
            #endregion

            if (!ValidateUser(username, password))
            {
                return false;
            }

            User user = UserController.GetUserByUsername(username);

            if (user == null)
            {
                throw new MembershipPasswordException("The supplied user name is not found.");
            }

            user.PasswordQuestion = newPasswordQuestion;
            user.PasswordAnswer = EncodeString(newPasswordAnswer);
            UserController.Update(user);

            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="email"></param>
        /// <param name="passwordQuestion"></param>
        /// <param name="passwordAnswer"></param>
        /// <param name="isApproved"></param>
        /// <param name="providerUserKey"></param>
        /// <param name="status"></param>
        /// <returns></returns>
        public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
        {
            #region Validation
            if (String.IsNullOrEmpty(username))
            {
                throw new ArgumentNullException("username");
            }

            if (String.IsNullOrEmpty(password))
            {
                throw new ArgumentNullException("password");
            }

            if (String.IsNullOrEmpty(email))
            {
                throw new ArgumentNullException("email");
            }

            if (RequiresQuestionAndAnswer)
            {
                if (String.IsNullOrEmpty(passwordQuestion))
                {
                    throw new ArgumentNullException("passwordQuestion");
                }

                if (String.IsNullOrEmpty(passwordAnswer))
                {
                    throw new ArgumentNullException("passwordAnswer");
                }
            }
            #endregion

            ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, password, true);
            OnValidatingPassword(args);

            if (args.Cancel)
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            }

            if (RequiresUniqueEmail && UserController.GetUserByEmail(email) != null)
            {
                status = MembershipCreateStatus.DuplicateEmail;
                return null;
            }

            if (UserController.GetUserByUsername(username) != null)
            {
                status = MembershipCreateStatus.DuplicateUserName;
                return null;
            }

            DateTime creationDate = DateTime.Now;
            User user = new User();

            user.Username = username;
            user.Password = EncodeString(password);
            user.Email = email;

            if (RequiresQuestionAndAnswer)
            {
                user.PasswordQuestion = passwordQuestion;
                user.PasswordAnswer = EncodeString(passwordAnswer);
            }
                
            user.IsApproved = isApproved;
            user.CreationDate = creationDate;
            user.LastPasswordChangedDate = creationDate;
            user.LastActivityDate = creationDate;
            user.IsLockedOut = false;

            UserController.Insert(user);
            status = MembershipCreateStatus.Success;

            return GetUser(username, false);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="username"></param>
        /// <param name="deleteAllRelatedData"></param>
        /// <returns></returns>
        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            #region Validation
            if (String.IsNullOrEmpty(username))
            {
                throw new ArgumentNullException("username");
            }
            #endregion

            User user = UserController.GetUserByUsername(username);

            if (user == null)
            {
                throw new MembershipPasswordException("The supplied user name is not found.");
            }

            UserController.Delete(user);
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        public override bool EnablePasswordReset
        {
            get { return m_enablePasswordReset; }
        }

        /// <summary>
        /// 
        /// </summary>
        public override bool EnablePasswordRetrieval
        {
            get { return m_enablePasswordRetrieval; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="emailToMatch"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="totalRecords"></param>
        /// <returns></returns>
        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            #region Validation
            if (pageSize <= 0)
            {
                throw new ArgumentOutOfRangeException("pageSize");
            }

            if (pageIndex < 0)
            {
                throw new ArgumentOutOfRangeException("pageIndex");
            }
            #endregion

            MembershipUserCollection membershipUsers = new MembershipUserCollection();
            IQueryable<User> users = UserController.FindUsersByEmail(emailToMatch, pageSize, pageIndex * pageSize);

            foreach (User user in users)
            {
                membershipUsers.Add(GetMembershipUser(user));
            }

            totalRecords = membershipUsers.Count;

            return membershipUsers;
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="usernameToMatch"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="totalRecords"></param>
        /// <returns></returns>
        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            #region Validation
            if (pageSize <= 0)
            {
                throw new ArgumentOutOfRangeException("pageSize");
            }

            if (pageIndex < 0)
            {
                throw new ArgumentOutOfRangeException("pageIndex");
            }
            #endregion

            MembershipUserCollection membershipUsers = new MembershipUserCollection();
            IQueryable<User> users = UserController.FindUsersByName(usernameToMatch, pageSize, pageIndex * pageSize);

            foreach (User user in users)
            {
                membershipUsers.Add(GetMembershipUser(user));
            }

            totalRecords = membershipUsers.Count;

            return membershipUsers;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="totalRecords"></param>
        /// <returns></returns>
        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            #region Validation
            if (pageSize <= 0)
            {
                throw new ArgumentOutOfRangeException("pageSize");
            }

            if (pageIndex < 0)
            {
                throw new ArgumentOutOfRangeException("pageIndex");
            }
            #endregion

            MembershipUserCollection membershipUsers = new MembershipUserCollection();
            IQueryable<User> users = UserController.SelectAll(pageSize, pageIndex * pageSize);

            foreach (User user in users)
            {
                membershipUsers.Add(GetMembershipUser(user));
            }

            totalRecords = membershipUsers.Count;

            return membershipUsers;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override int GetNumberOfUsersOnline()
        {
            TimeSpan onlineSpan = new TimeSpan(0, System.Web.Security.Membership.UserIsOnlineTimeWindow, 0);
            DateTime compareTime = DateTime.Now.Subtract(onlineSpan);

            int result = UserController.GetNumberOfUsersOnline(compareTime);

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="username"></param>
        /// <param name="answer"></param>
        /// <returns></returns>
        public override string GetPassword(string username, string answer)
        {
            #region Validation
            if (String.IsNullOrEmpty(username))
            {
                throw new ArgumentNullException("username");
            }

            if (RequiresQuestionAndAnswer && String.IsNullOrEmpty(answer))
            {
                throw new ArgumentNullException("answer");
            }
            #endregion

            if (!EnablePasswordRetrieval)
            {
                throw new ProviderException("Password retrieval not enabled.");
            }

            if (PasswordFormat == MembershipPasswordFormat.Hashed)
            {
                throw new ProviderException("Cannot retrieve hashed passwords.");
            }

            User user = UserController.GetUserByUsername(username);

            if (user == null)
            {
                throw new MembershipPasswordException("The supplied user name is not found.");
            }

            if (user.IsLockedOut)
            {
                throw new MembershipPasswordException("The supplied user is locked out.");
            }

            if (RequiresQuestionAndAnswer && !CompareEncryptedString(answer, user.PasswordAnswer))
            {
                throw new MembershipPasswordException("Incorrect password answer.");
            }

            string password = user.Password;

            if (PasswordFormat == MembershipPasswordFormat.Encrypted)
            {
                password = UnEncodeString(user.Password);
            }

            return password;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="username"></param>
        /// <param name="userIsOnline"></param>
        /// <returns></returns>
        public override MembershipUser GetUser(string username, bool userIsOnline)
        {
            #region Validation
            if (String.IsNullOrEmpty(username))
            {
                throw new ArgumentNullException("username");
            }
            #endregion

            User user = UserController.GetUserByUsername(username);

            if (user == null)
            {
                throw new MembershipPasswordException("The supplied user name is not found.");
            }

            if (userIsOnline)
            {
                user.LastActivityDate = DateTime.Now;
                UserController.Update(user);
            }

            MembershipUser membershipUser = GetMembershipUser(user);

            return membershipUser;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="providerUserKey"></param>
        /// <param name="userIsOnline"></param>
        /// <returns></returns>
        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {
            #region Validation
            if (providerUserKey == null)
            {
                throw new ArgumentNullException("providerUserKey");
            }
            #endregion

            User user = UserController.GetEntity(providerUserKey);

            if (user == null)
            {
                throw new MembershipPasswordException("The supplied user name is not found.");
            }

            if (userIsOnline)
            {
                user.LastActivityDate = DateTime.Now;
                UserController.Update(user);
            }

            MembershipUser membershipUser = GetMembershipUser(user);

            return membershipUser;

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="email"></param>
        /// <returns></returns>
        public override string GetUserNameByEmail(string email)
        {
            #region Validation
            if (String.IsNullOrEmpty(email))
            {
                throw new ArgumentNullException("email");
            }
            #endregion

            User user = UserController.GetUserByEmail(email);

            if (user == null)
            {
                throw new MembershipPasswordException("The supplied user name is not found.");
            }

            return user.Username;
        }

        /// <summary>
        /// 
        /// </summary>
        public override int MaxInvalidPasswordAttempts
        {
            get { return m_maxInvalidPasswordAttempts; }
        }

        /// <summary>
        /// 
        /// </summary>
        public override int MinRequiredNonAlphanumericCharacters
        {
            get { return m_minRequiredNonAlphanumericCharacters; }
        }

        /// <summary>
        /// 
        /// </summary>
        public override int MinRequiredPasswordLength
        {
            get { return m_minRequiredPasswordLength; }
        }

        /// <summary>
        /// 
        /// </summary>
        public override int PasswordAttemptWindow
        {
            get { return m_passwordAttemptWindow; }
        }

        /// <summary>
        /// 
        /// </summary>
        public override MembershipPasswordFormat PasswordFormat
        {
            get { return m_passwordFormat; }
        }

        /// <summary>
        /// 
        /// </summary>
        public override string PasswordStrengthRegularExpression
        {
            get { return m_passwordStrengthRegularExpression; }
        }

        /// <summary>
        /// 
        /// </summary>
        public override bool RequiresQuestionAndAnswer
        {
            get { return m_requiresQuestionAndAnswer; }
        }

        /// <summary>
        /// 
        /// </summary>
        public override bool RequiresUniqueEmail
        {
            get { return m_requiresUniqueEmail; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="username"></param>
        /// <param name="answer"></param>
        /// <returns></returns>
        public override string ResetPassword(string username, string answer)
        {
            #region Validation
            if (String.IsNullOrEmpty(username))
            {
                throw new ArgumentNullException("email");
            }

            if (RequiresQuestionAndAnswer && String.IsNullOrEmpty(answer))
            {
                throw new ArgumentNullException("answer");
            }
            #endregion

            if (!EnablePasswordReset)
            {
                throw new NotSupportedException("Password reset is not enabled.");
            }

            string newPassword = System.Web.Security.Membership.GeneratePassword(MinRequiredPasswordLength, MinRequiredNonAlphanumericCharacters);
            ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, newPassword, true);
            OnValidatingPassword(args);

            if (args.Cancel)
            {
                if (args.FailureInformation != null)
                {
                    throw args.FailureInformation;
                }
                else
                {
                    throw new MembershipPasswordException("Reset password canceled due to password validation failure.");
                }
            }

            User user = UserController.GetUserByUsername(username);

            if (user == null)
            {
                throw new MembershipPasswordException("The supplied user name is not found.");
            }

            if (user.IsLockedOut)
            {
                throw new MembershipPasswordException("The supplied user is locked out.");
            }

            if (RequiresQuestionAndAnswer && !CompareEncryptedString(answer, user.PasswordAnswer))
            {
                throw new MembershipPasswordException("Incorrect password answer.");
            }

            user.Password = EncodeString(newPassword);
            UserController.Update(user);

            return newPassword;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public override bool UnlockUser(string userName)
        {
            #region Validation
            if (String.IsNullOrEmpty(userName))
            {
                throw new ArgumentNullException("userName");
            }
            #endregion

            User user = UserController.GetUserByUsername(userName);

            if (user == null)
            {
                throw new MembershipPasswordException("The supplied user name is not found.");
            }

            user.IsLockedOut = false;
            UserController.Update(user);

            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="user"></param>
        public override void UpdateUser(MembershipUser user)
        {
            #region Validation
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            #endregion

            User u = UserController.GetUserByUsername(user.UserName);

            if (user == null)
            {
                throw new MembershipPasswordException("The supplied user name is not found.");
            }

            u.Email = user.Email;
            u.Comment = user.Comment;
            u.IsApproved = user.IsApproved;

            UserController.Update(u);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public override bool ValidateUser(string username, string password)
        {
            #region Validation
            if (String.IsNullOrEmpty(username))
            {
                throw new ArgumentNullException("username");
            }

            if (String.IsNullOrEmpty(password))
            {
                throw new ArgumentNullException("password");
            }
            #endregion

            return ValidateUser(username, password, false);
        }
		
		#endregion
		
		#region Private Methods

        private MembershipPasswordFormat GetPasswordFormat(string configValue, string defaultValue)
        {
            string value = ProviderHelper.GetConfigValue(configValue, defaultValue);

            switch (value)
            {
                case "Hashed":
                    return MembershipPasswordFormat.Hashed;
                case "Encrypted":
                    return MembershipPasswordFormat.Encrypted;
                case "Clear":
                    return MembershipPasswordFormat.Clear;
                default:
                    throw new ProviderException("Password format not supported.");
            }
        }

        private byte[] HexToByte(string source)
        {
            #region Validation
            if (String.IsNullOrEmpty(source))
            {
                throw new ArgumentNullException("source");
            }
            #endregion

            byte[] returnBytes = new byte[source.Length / 2];

            for (int i = 0; i < returnBytes.Length; i++)
            {
                returnBytes[i] = Convert.ToByte(source.Substring(i * 2, 2), 16);
            }

            return returnBytes;
        }

        private string EncodeString(string source)
        {
            #region Validation
            if (String.IsNullOrEmpty(source))
            {
                throw new ArgumentNullException("source");
            }
            #endregion

            string encoded = source;

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Clear:
                    break;
                case MembershipPasswordFormat.Encrypted:
                    encoded = Convert.ToBase64String(EncryptPassword(Encoding.Unicode.GetBytes(source)));
                    break;
                case MembershipPasswordFormat.Hashed:
                    HMACSHA1 hash = new HMACSHA1();

                    hash.Key = HexToByte(machineKey.ValidationKey);
                    encoded = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(source)));
                    break;
                default:
                    throw new ProviderException("Unsupported password format.");
            }

            return encoded;
        }

        private MembershipUser GetMembershipUser(User user)
        {
            #region Validation
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            #endregion

            MembershipUser membershipUser = new MembershipUser(Name,
                                                               user.Username,
                                                               user.Id,
                                                               user.Email,
                                                               user.PasswordQuestion,
                                                               user.Comment,
                                                               user.IsApproved.GetValueOrDefault(),
                                                               user.IsLockedOut,
                                                               user.CreationDate,
                                                               user.LastLoginDate.GetValueOrDefault(),
                                                               user.LastActivityDate.GetValueOrDefault(),
                                                               user.LastPasswordChangedDate.GetValueOrDefault(),
                                                               user.LastLockedOutDate.GetValueOrDefault());

            return membershipUser;
        }

        private bool CompareEncryptedString(string givenString, string storedString)
        {
            #region Validation
            if (String.IsNullOrEmpty(givenString))
            {
                throw new ArgumentNullException("givenString");
            }

            if (String.IsNullOrEmpty(storedString))
            {
                throw new ArgumentNullException("storedString");
            }
            #endregion

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Encrypted:
                    storedString = UnEncodeString(storedString);
                    break;
                case MembershipPasswordFormat.Hashed:
                    givenString = EncodeString(givenString);
                    break;
                default:
                    break;
            }

            bool result = givenString == storedString;

            return result;
        }

        private string UnEncodeString(string source)
        {
            #region Validation
            if (String.IsNullOrEmpty(source))
            {
                throw new ArgumentNullException("source");
            }
            #endregion

            string password = source;

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Clear:
                    break;
                case MembershipPasswordFormat.Encrypted:
                    password =
                      Encoding.Unicode.GetString(DecryptPassword(Convert.FromBase64String(password)));
                    break;
                case MembershipPasswordFormat.Hashed:
                    throw new ProviderException("Cannot unencode a hashed password.");
                default:
                    throw new ProviderException("Unsupported password format.");
            }

            return password;

        }

        private bool ValidateUser(string username, string password, bool updateLastLogin)
        {
            #region Validation
            if (String.IsNullOrEmpty(username))
            {
                throw new ArgumentNullException("username");
            }

            if (String.IsNullOrEmpty(password))
            {
                throw new ArgumentNullException("password");
            }
            #endregion

            User user = UserController.GetUserByUsername(username);

            if (user == null)
            {
                return false;
            }

            if (!CompareEncryptedString(password, user.Password))
            {
                return false;
            }

            if (!user.IsApproved.GetValueOrDefault())
            {
                return false;
            }

            if (updateLastLogin)
            {
                user.LastLoginDate = DateTime.Now;
                UserController.Update(user);
            }

            return true;
        }
		
		#endregion
    }
}
